#include "stdafx.h"
#include "AlgorithmHolder.h"


Library::AlgorithmsHolder::AlgorithmsHolder(double step, double accuracy_ )
: step_(step ), accuracy_(accuracy_)
{
}

void Library::AlgorithmsHolder::SimpleHillClimbing( FunctionHolder& func )
{
    std::vector<std::pair<double, double> > mutablePoints = func.GetMutablePoints();
    int iterations = 0;
	double step = step_;
    double accuracy = accuracy_;
    while (step > accuracy)
    {
        bool posibleMakeBetterOnePointAtLeast = false;
        for (size_t i = 0; i < mutablePoints.size(); ++i)
        {
            posibleMakeBetterOnePointAtLeast |= HillClimbingImprovePoint(func, mutablePoints, i, step);
            func.SetMutablePoints(mutablePoints);
            func.Calculate();
        }

        if (!posibleMakeBetterOnePointAtLeast)
        {
            step /= 2.0;
        }
        ++iterations;
    }
}

void Library::AlgorithmsHolder::MultistartHillClimbing(FunctionHolder& func, int numberOfMultistarts)
{
	SimpleHillClimbing(func);
	double best = func.GetResult();
	std::vector<std::pair<int, int> > edges = func.GetEdges();
	for (int i = 1; i < numberOfMultistarts; ++i)
	{
		func.ResetMutablePoints();
		SimpleHillClimbing(func);
		if (best > func.GetResult())
		{
			edges = func.GetEdges();
			best  = func.GetResult();
		}
	}
	func.SetEdges(edges);
}

bool Library::AlgorithmsHolder::HillClimbingImprovePoint( FunctionHolder& func, std::vector<std::pair<double, double> >& pointsToImprove, const size_t pointNumber, double step)
{
    bool isPosibleToImprove = false;
    Directions direction = D_NONE;

    std::pair<double, double> oldPointValue = pointsToImprove.at(pointNumber);
    func.Calculate();
    double best = func.GetResult();

    pointsToImprove.at(pointNumber).first -= step;
    func.SetMutablePoints(pointsToImprove);
    func.Calculate();
    double stepLeftValue = func.GetResult();
    if (stepLeftValue < best)
    {
        best = stepLeftValue;
        direction = D_LEFT;
        isPosibleToImprove = true;
    }
    
    pointsToImprove.at(pointNumber).first += 2 * step;
    func.SetMutablePoints(pointsToImprove);
    func.Calculate();
    double stepRightValue = func.GetResult();
    if (stepRightValue < best)
    {
        best = stepRightValue;
        direction = D_RIGHT;
        isPosibleToImprove = true;
    }

    pointsToImprove.at(pointNumber).first -= step;
    pointsToImprove.at(pointNumber).second += step;
    func.SetMutablePoints(pointsToImprove);
    func.Calculate();
    double stepUpValue = func.GetResult();
    if (stepUpValue < best)
    {
        best = stepUpValue;
        direction = D_UP;
        isPosibleToImprove = true;
    }


    pointsToImprove.at(pointNumber).second -= 2 * step;
    func.SetMutablePoints(pointsToImprove);
    func.Calculate();
    double stepDownValue = func.GetResult();
    if (stepDownValue < best)
    {
        best = stepDownValue;
        direction = D_DOWN;
        isPosibleToImprove = true;
    }
    pointsToImprove.at(pointNumber).second += step;

    switch (direction)
    {
    case D_DOWN:
        pointsToImprove.at(pointNumber).second -= step;
        break;
    case D_LEFT:
        pointsToImprove.at(pointNumber).first -= step;
        break;
    case D_RIGHT:
        pointsToImprove.at(pointNumber).first += step;
        break;
    case D_UP:
        pointsToImprove.at(pointNumber).second += step;
        break;

    }

    return isPosibleToImprove;
}